package com.flying.invest.strategy.service;

import java.util.Date;
import java.util.List;

import com.flying.common.annotation.DaoCreate;
import com.flying.common.annotation.DaoQuery;
import com.flying.common.annotation.DaoRemove;
import com.flying.common.annotation.DaoUpdate;
import com.flying.common.annotation.Transaction;
import com.flying.common.util.Codes;
import com.flying.framework.annotation.MethodInfo;
import com.flying.framework.annotation.Param;
import com.flying.framework.annotation.Service;
import com.flying.framework.annotation.event.MethodListener;
import com.flying.framework.annotation.event.MethodListenerHandler;
import com.flying.framework.context.ServiceContext;
import com.flying.framework.data.Data;
import com.flying.framework.exception.AppException;
import com.flying.framework.model.ActionResult;
import com.flying.framework.model.ModelFactory;
import com.flying.framework.model.QueryResult;
import com.flying.framework.security.Principal;
import com.flying.framework.service.AbstractService;
import com.flying.invest.invest.model.Investment;
import com.flying.invest.invest.model.InvestmentSuggestion;
import com.flying.invest.invest.service.InvestmentService;
import com.flying.invest.invest.service.InvestmentSuggestionService;
import com.flying.invest.strategy.model.Strategy;
import com.flying.invest.strategy.model.Strategy.Scope;
import com.flying.invest.strategy.model.Strategy.Status;
import com.flying.invest.strategy.model.StrategyType;
import com.flying.invest.strategy.model.StrategyType.SensitiveType;
import com.flying.invest.strategy.vo.StrategyVO;

@Service(value="StrategyService", desc="交易策略")
public class StrategyService extends AbstractService{
	private InvestmentService investService;
	private InvestmentSuggestionService suggestionService;
	private StrategyInvestMonitorService monitorService;
	
	public void setInvestmentService(InvestmentService s) {
		this.investService = s;
	}
	public void setInvestmentSuggestionService(InvestmentSuggestionService s) {
		this.suggestionService = s;
	}
	public void setStrategyInvestMonitorService(StrategyInvestMonitorService s) {
		this.monitorService = s;
	}
        
	@MethodInfo("新增")
	@DaoCreate(entity=Strategy.ENTITY_NAME)
	protected Strategy create2(
			@Param(value=Strategy.USER_ID, required=false,	desc="所属用户") Long user_id,
			@Param(value=Strategy.TYPE_ID, required=true,	desc="策略类型") Long type_id,
			@Param(value=Strategy.INVEST_ID, required=false,	desc="投资项目") Long invest_id,
			@Param(value=Strategy.ASSETS_ID, required=false,	desc="投资品种") Long assets_id,
			@Param(value=Strategy.NAME, required=false,	desc="名称") String name,
			@Param(value=Strategy.REMARKS, required=false,	desc="说明") String remarks,
			
			@Param(value=Strategy.SCOPE, required=false,	desc="范围（0：公共；1：专属）", enumClass=Scope.class) Integer scope,
			@Param(value=Strategy.STATUS, required=false,	desc="状态（0：待执行；1：执行中；2：成功结束）", enumClass=Status.class) Integer status,
			@Param(value=Strategy.EFFECTIVE_FROM, required=false,	desc="生效时间") Date effective_from,
			@Param(value=Strategy.EFFECTIVE_TO, required=false,	desc="失效时间") Date effective_to,
			@Param(value=Strategy.CREATOR, required=false,	desc="创建人") String creator,
			@Param(value=Strategy.CREATE_TIME, required=false,	desc="创建时间") Date create_time
		
		) {
		Principal p = ServiceContext.getContext().getPrincipal();
		if(user_id == null) user_id = Long.parseLong(p.getId());
		if(creator == null) creator = p.getName();
		if(create_time == null) create_time = new Date();
		if(name == null) {
			Investment t = this.investService.findById(invest_id);
			name = t.getAssetsName();
		}
		return ModelFactory.create(Strategy.class, 
				Strategy.USER_ID, user_id, 
				Strategy.NAME, name, 
				Strategy.SCOPE, scope == null? Scope.STRATEGY_SCOPE.value: scope, 
				Strategy.STATUS, status == null? Status.PROCESSING.value: status, 
				Strategy.CREATOR, creator, 
				Strategy.CREATE_TIME, create_time);
	}
    
	@MethodInfo("新增")
	@MethodListener(handlers={@MethodListenerHandler(serviceId="InvestmentService:updateIncludeStrategies")})
	@Transaction
	public Strategy create(
			@Param(value=Strategy.USER_ID, required=false,	desc="所属用户") Long user_id,
			@Param(value=Strategy.TYPE_ID, required=true,	desc="策略类型") Long type_id,
			@Param(value=Strategy.INVEST_ID, required=true,	desc="投资项目") Long invest_id,
			@Param(value=Strategy.NAME, required=false,	desc="名称") String name,
			@Param(value=Strategy.REMARKS, required=false,	desc="说明") String remarks,
			
			@Param(value=Strategy.SCOPE, required=false,	desc="范围（0：公共；1：专属）", enumClass=Scope.class) Integer scope,
			@Param(value=Strategy.STATUS, required=false,	desc="状态（0：待执行；1：执行中；2：成功结束）", enumClass=Status.class) Integer status,
			@Param(value=Strategy.EFFECTIVE_FROM, required=false,	desc="生效时间") Date effective_from,
			@Param(value=Strategy.EFFECTIVE_TO, required=false,	desc="失效时间") Date effective_to,
			@Param(value=Strategy.CREATOR, required=false,	desc="创建人") String creator,
			@Param(value=Strategy.CREATE_TIME, required=false,	desc="创建时间") Date create_time
		
		) throws Exception {
		Investment v = this.investService.findById(invest_id);
		Strategy s = this.create2(user_id, type_id, invest_id, v.getAssetsId(), name, remarks, scope, status, effective_from, effective_to, creator, create_time);
		String sn = s.getStrategyType().getService();
		IStrategyService sv = module.getService(sn);
		sv.createSub(s.getStrategyId());
		return s;
	}

	@MethodInfo("修改")
	@DaoUpdate(entity=Strategy.ENTITY_NAME)
	public ActionResult updateInvestInfo(
			@Param(value=Strategy.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id,
			@Param(value=Strategy.INVEST_ID, required=false,	desc="投资项目") Long invest_id,
			@Param(value=Strategy.ASSETS_ID, required=false,	desc="投资品种") Long assets_id
			
		) {
		var strategy = this.findById(strategy_id);
		if(strategy.getStatus() == Status.OVER.value()) {
			throw new AppException("0101", strategy.getName()+"已经经结束，不能修改！");
		}
		return null;
	}

	@MethodInfo("修改")
	@MethodListener(handlers={@MethodListenerHandler(serviceId="StrategyService:updateIncludeStrategies")})
	@Transaction
	@DaoUpdate(entity=Strategy.ENTITY_NAME)
	public ActionResult update(
			@Param(value=Strategy.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id,
			@Param(value=Strategy.NAME, required=false,	desc="名称") String name,
			@Param(value=Strategy.REMARKS, required=false,	desc="说明") String remarks,
			
			@Param(value=Strategy.SCOPE, required=false,	desc="范围（0：公共；1：专属）") Integer scope,
			@Param(value=Strategy.STATUS, required=true,	desc="状态", enumClass=Status.class) Integer status,
			@Param(value=Strategy.EFFECTIVE_FROM, required=false,	desc="生效时间") Date effective_from,
			@Param(value=Strategy.EFFECTIVE_TO, required=false,	desc="失效时间") Date effective_to,
			
			@Param(value=Strategy.BATCH_NUM, required=false,	desc="最后批号") Integer batch_num,
			@Param(value=Strategy.UPDATOR, required=false,	desc="修改人") String updator,
			@Param(value=Strategy.UPDATE_TIME, required=false,	desc="修改时间") Date update_time
			
		) {
		var strategy = this.findById(strategy_id);
		if(strategy.getStatus() == Status.OVER.value()) {
			throw new AppException("0101", strategy.getName()+"已经经结束，不能修改！");
		}
		if(name == null) {
			name = strategy.getName();
		}
		Principal p = ServiceContext.getContext().getPrincipal();
		if(updator == null) updator = p.getName();
		if(update_time == null) update_time = new Date();
		return ModelFactory.create(ActionResult.class, 
				Strategy.SCOPE, scope == null? Scope.STRATEGY_SCOPE.value: scope, 
				Strategy.NAME, name, 
				Strategy.UPDATOR, updator, 
				Strategy.UPDATE_TIME, update_time);
	}

	@MethodInfo("修改")
	@DaoUpdate(entity=Strategy.ENTITY_NAME)
	protected ActionResult updateForClose(
			@Param(value=Strategy.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id,
			@Param(value=Strategy.REMARKS, required=false,	desc="说明") String remarks,
			@Param(value=Strategy.STATUS, required=true,	desc="状态", enumClass=Status.class) Integer status,
			@Param(value=Strategy.UPDATOR, required=false,	desc="修改人") String updator,
			@Param(value=Strategy.UPDATE_TIME, required=false,	desc="修改时间") Date update_time) {
		return null;
	}

	@Transaction
	public ActionResult closeAllActivesByInvestId(
			@Param(value=Strategy.INVEST_ID, required=true,	desc="投资项目") Long invest_id,
			@Param(value=Strategy.UPDATOR, required=false,	desc="修改人") String updator,
			@Param(value=Strategy.UPDATE_TIME, required=false,	desc="修改时间") Date update_time) {
		List<Strategy> list = this.findAllActivesByInvestId(invest_id, new Integer[] {Status.PREPARING.value, Status.PROCESSING.value}, 0, 10000).getRows();
		list.stream().forEach(x -> {
			this.updateForClose(x.getStrategyId(), "Closed for investment!", Status.OVER.value, updator, update_time);
		});
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}

	@MethodInfo("修改")
	@DaoUpdate(entity=Strategy.ENTITY_NAME)
	protected ActionResult updateExecutions(
			@Param(value=Strategy.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id,
			@Param(value=Strategy.EXECUTE_TIME, required=true,	desc="失效时间") Date execute_time,
			@Param(value=Strategy.BATCH_NUM, required=true,	desc="最后批号") Integer batch_num) {
		return null;
	}

	@MethodInfo("修改")
	public ActionResult updateIncludeStrategies(
			@Param(value=Strategy.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id) {
		var s = this.findById(strategy_id);
		return this.investService.updateIncludeStrategies(s.getInvestId(), null);
	}

	@MethodInfo("删除")
	@DaoRemove(entity=Strategy.ENTITY_NAME)
	protected ActionResult remove2(
			@Param(value=Strategy.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id
		) {
		return null;  
	}

	@MethodInfo("删除")
	@Transaction
	public ActionResult remove(
			@Param(value=Strategy.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id
		) {
		Strategy s = this.findById(strategy_id);
		String sn = s.getStrategyType().getService();
		IStrategyService service = module.getService(sn);
		service.remove(strategy_id);
		this.remove2(strategy_id);
		return this.investService.updateIncludeStrategies(s.getInvestId(), null);
	}

	@MethodInfo("删除")
	public ActionResult removeByInvestId(
			@Param(value=Strategy.INVEST_ID, required=false,	desc="投资项目") Long invest_id
		) {
		List<Strategy> ss = this.findAll(null, null, invest_id, null, 0, Integer.MAX_VALUE).getRows();
		ss.stream().forEach(x -> {
			this.remove(x.getInvestId());
		});
		return null;
	}

	@SuppressWarnings("unchecked")
	@MethodInfo("搜索")
	@DaoQuery(entity=Strategy.ENTITY_NAME, pageable=true, modelClass=Strategy.class, wsql="type_id != 8")
	public QueryResult<Strategy> findAll(
			@Param(value=Strategy.USER_ID, required=false,	desc="所属用户") Long user_id,
			@Param(value=Strategy.TYPE_ID, required=false,	desc="策略类型") Long type_id,
			@Param(value=Strategy.INVEST_ID, required=false,	desc="投资项目") Long invest_id,
			@Param(value=Strategy.STATUS, required=false,	desc="状态", enumClass=Status.class) Integer status,
			@Param(value=Codes.PAGE_NUM, 	required=false, desc="分页页号，默认0") int page,
			@Param(value=Codes.PAGE_SIZE, 	required=false, desc="分页大小，默认10") int rows) {
		Principal p = ServiceContext.getContext().getPrincipal();
		if(user_id == null && p != null) user_id = Long.parseLong(p.getId());
		return ModelFactory.create(QueryResult.class, Strategy.USER_ID, user_id);
	}

	@MethodInfo("搜索")
	@DaoQuery(entity=Strategy.ENTITY_NAME, pageable=true, modelClass=Strategy.class)
	public QueryResult<Strategy> findAllActivesByInvestId(
			@Param(value=Strategy.INVEST_ID, required=false,	desc="投资项目") Long invest_id,
			@Param(value=Strategy.STATUS, required=false,	desc="状态", enumClass=Status.class, tag="IN") Integer[] status,
			@Param(value=Codes.PAGE_NUM, 	required=false, desc="分页页号，默认0") int page,
			@Param(value=Codes.PAGE_SIZE, 	required=false, desc="分页大小，默认10") int rows) {
		return null;
	}

	@MethodInfo("搜索")
	@DaoQuery(entity=Strategy.ENTITY_NAME, pageable=false, modelClass=Strategy.class)
	public QueryResult<Strategy> findByInvestId(
			@Param(value=Strategy.INVEST_ID, required=false,	desc="投资项目") Long invest_id) {
		return null;
	}

	@MethodInfo("查询")
	@DaoQuery(entity=Strategy.ENTITY_NAME, single=true, throwsNotFoundException=true )
	public Strategy findById(
			@Param(value=Strategy.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id
		) {
		return null;
	}

	@MethodInfo("查询")
	@DaoQuery(entity=Strategy.ENTITY_NAME, single=true, throwsNotFoundException=true )
	public StrategyVO findById2(
			@Param(value=Strategy.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id
		) {
		return null;
	}

	@MethodInfo("查询")
	public <T extends Data>T findConfigById(
			@Param(value=Strategy.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id
		) {
		String sn = this.findById(strategy_id).getStrategyType().getService();
		IStrategyService s = module.getService(sn);
		return s.findById(strategy_id);
	}
	
	//
	public ActionResult executeStrategy(
			@Param(value=Strategy.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id) {
		var strategy = this.findById(strategy_id);
		return this.innerExecuteStrategy(strategy);
	}
	
	public ActionResult executeAllStrategies(
			@Param(value=Strategy.USER_ID, required=false,	desc="所属用户") Long user_id,
			@Param(value=Strategy.TYPE_ID, required=false,	desc="策略类型") Long type_id) {
		var p = ServiceContext.getContext().getPrincipal();
		if(user_id == null && p != null) {
			user_id = Long.parseLong(p.getId());
		}
		if(type_id == null || type_id != null && type_id != 8L) {
			this.findAll(user_id, type_id, null, Status.PROCESSING.value(), 0, 10000).getRows().stream().forEach(s -> {
				try {
					this.innerExecuteStrategy(s);
				} catch (Exception e) {
					e.printStackTrace();
				}
			});
		} else if(type_id == 8L) {
			this.monitorService.findAll(user_id, null, null, null, 0, Integer.MAX_VALUE).getRows().stream().forEach(s -> {
				try {
					this.innerExecuteStrategy(s);
				} catch (Exception e) {
					e.printStackTrace();
				}
			});
		}
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}

	@Transaction
	protected ActionResult innerExecuteStrategy(Strategy strategy) {
		if(strategy.getStatus() == Strategy.Status.PREPARING.value() || strategy.getStatus() == Strategy.Status.OVER.value()) {
			return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
		}
		if(strategy.getInvestId() != null) {
			//最近一条建议未执行，不再重新生成；
			var suggestions = this.suggestionService.findByStrategyId(strategy.getStrategyId(), 
					new Integer[]{InvestmentSuggestion.Status.PREPARING.value(), InvestmentSuggestion.Status.ORDERED.value()}).getRows();
			if(suggestions.size() > 0) {
				return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
			}
		}
		var serviceId = strategy.getStrategyType().getService();
		IStrategyService s = module.getService(serviceId);
		s.executeStrategy(strategy.getStrategyId());
		return this.updateExecutions(strategy.getStrategyId(), new Date(), strategy.getBatchNum() == null?1: strategy.getBatchNum() + 1);
	}
	
	public ActionResult transactionCallback(Long strategy_id, Long transaction_id, Integer transaction_status, 
			Integer transaction_type, Double quantity, Double price, Double amount) {
		var serviceId = this.findById(strategy_id).getStrategyType().getService();
		var service = module.getService(serviceId);
		if(service instanceof ITransactionCallbackService) {
			((ITransactionCallbackService)service).transactionCallback(strategy_id, transaction_id, 
					transaction_status, transaction_type, quantity, price, amount);
		}
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}

	@Transaction
	public ActionResult flushDetails(
			@Param(value=Strategy.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id) {
		Strategy g = this.findById(strategy_id);
		StrategyType t = g.getStrategyType();
		IStrategyService s = module.getService(t.getService());
		if(t.getSensitive() == SensitiveType.AMOUNT.value() || 
				t.getSensitive() == SensitiveType.PRICE.value() || 
				t.getSensitive() == SensitiveType.BOTH.value()) {
			s.flushDetails(strategy_id);
		}
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}
}